package com.lhl.shiro.shiroconfig;


import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * @author LHL
 * @date 2018/1/8
 */


public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
    @Autowired
    private RedisTemplate redisTemplate;
    @Value("${user.login.lock.time}")
    private Integer lockTime;

    private static final Logger LOGGER = LoggerFactory.getLogger(RetryLimitHashedCredentialsMatcher.class);

    public RetryLimitHashedCredentialsMatcher(String hashAlgorithm, int hashIterations, boolean storedCredentialsHexEncoded) {
        setHashAlgorithmName(hashAlgorithm);
        setHashIterations(hashIterations);
        setStoredCredentialsHexEncoded(storedCredentialsHexEncoded);
    }

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        String username = (String) token.getPrincipal();
        LOGGER.debug("[开始登录用户名密码验证] username={},password={}", username, token.getCredentials());
        AtomicInteger retryCount = (AtomicInteger) redisTemplate.opsForValue().get(username);
        if (retryCount == null) {
            retryCount = new AtomicInteger(0);
        }
        if (retryCount.incrementAndGet() > 10) {
            redisTemplate.expire(username, lockTime, TimeUnit.MINUTES);
            //if retry count > 20 throw
            LOGGER.error("用户连续密码错误超过10次，账号被锁定10分钟，username:{}", username);
            throw new ExcessiveAttemptsException("用户名" + username + "密码连续错误超过10次，已被锁定");
        }
        redisTemplate.opsForValue().set(username, retryCount);
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
            //clear retry count
            LOGGER.debug("[开始登录用户名密码验证成功]");
            redisTemplate.delete(username);
        } else {
            LOGGER.debug("[开始登录用户名密码验证失败]");
            if (retryCount.get() == 1) {
                redisTemplate.expire(username, 24 * 60, TimeUnit.MINUTES);
            }
        }

        return matches;
    }
}
